#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/evp.h>
#include <errno.h>
#include"compute_hash.h"

#define BUFFER_SIZE 4096 // 每次读取 4KB
// #define SHA256_HEX_LENGTH (32 * 2 + 1) // SHA256 是 32 字节, 十六进制是 64 字符 + 1 个 '\0'

/**
 * @brief 将二进制哈希值转换为十六进制字符串
 *
 * @param hash 二进制哈希值缓冲区
 * @param hash_len 哈希值的字节长度
 * @param hex_hash 输出十六进制字符串的缓冲区 (确保大小足够)
 */
void hash_to_hex(const unsigned char *hash, unsigned int hash_len, char *hex_hash) {
    for (unsigned int i = 0; i < hash_len; i++) {
        sprintf(hex_hash + (i * 2), "%02x", hash[i]);
    }
    hex_hash[hash_len * 2] = '\0'; // 添加字符串结束符
}

/**
 * @brief 计算文件的 SHA-256 哈希值
 *
 * @param filepath 要计算哈希值的文件路径
 * @param hex_hash_out 用于存储结果（十六进制字符串）的缓冲区 (必须至少 65 字节)
 * @return 0 表示成功, -1 表示失败
 */
int calculate_file_sha256(const char *filepath, char *hex_hash_out) {
    FILE *file = NULL;
    EVP_MD_CTX *mdctx = NULL;
    const EVP_MD *md = NULL;
    unsigned char buffer[BUFFER_SIZE];
    size_t bytesRead = 0;
    unsigned char hash[EVP_MAX_MD_SIZE]; // EVP_MAX_MD_SIZE 足够大
    unsigned int hash_len = 0;
    int success = -1; // 默认失败

    // 1. 打开文件 (使用 "rb" 二进制模式!)
    file = fopen(filepath, "rb");
    if (file == NULL) {
        LOG_ERROR("Failed to open file: %s\n", filepath);
        perror("Error opening file");
        fprintf(stderr, "Failed to open file: %s\n", filepath);
        goto cleanup; // 使用 goto 进行统一清理
    }

    // 2. 获取 SHA-256 算法
    md = EVP_sha256();
    if (md == NULL) {
        LOG_ERROR("Failed to get SHA-256 algorithm.\n");
        fprintf(stderr, "Failed to get SHA-256 algorithm.\n");
        goto cleanup;
    }

    // 3. 创建并初始化 EVP 上下文
    mdctx = EVP_MD_CTX_new();
    if (mdctx == NULL) {
        LOG_ERROR("Failed to create EVP context.\n");
        fprintf(stderr, "Failed to create EVP context.\n");
        goto cleanup;
    }

    if (1 != EVP_DigestInit_ex(mdctx, md, NULL)) {
        LOG_ERROR("Failed to initialize EVP context.\n");
        fprintf(stderr, "Failed to initialize EVP context.\n");
        goto cleanup;
    }

    // 4. 循环读取文件并更新哈希值
    while ((bytesRead = fread(buffer, 1, BUFFER_SIZE, file)) != 0) {
        if (1 != EVP_DigestUpdate(mdctx, buffer, bytesRead)) {
            LOG_ERROR("Failed to update EVP context.\n");
            fprintf(stderr, "Failed to update EVP context.\n");
            goto cleanup;
        }
    }

    // 检查是否是因为错误而不是到达文件末尾
    if (ferror(file)) {
        LOG_ERROR("Error reading file: %s\n", filepath);
        fprintf(stderr, "Error reading file: %s\n", filepath);
        goto cleanup;
    }

    // 5. 完成哈希计算
    if (1 != EVP_DigestFinal_ex(mdctx, hash, &hash_len)) {
        LOG_ERROR("Failed to finalize EVP context.\n");
        fprintf(stderr, "Failed to finalize EVP context.\n");
        goto cleanup;
    }

    // 6. 将二进制哈希转换为十六进制字符串
    hash_to_hex(hash, hash_len, hex_hash_out);

    success = 0; // 所有步骤成功

cleanup:
    // 7. 清理资源
    if (mdctx != NULL) {
        EVP_MD_CTX_free(mdctx);
    }
    if (file != NULL) {
        fclose(file);
    }

    return success;
}

// // 主函数：示例用法
// int main(int argc, char *argv[]) {
//     if (argc != 2) {
//         fprintf(stderr, "Usage: %s <file_path>\n", argv[0]);
//         return 1;
//     }

//     const char *filepath = argv[1];
//     char hex_hash[SHA256_HEX_LENGTH]; // 准备足够大的缓冲区

//     if (calculate_file_sha256(filepath, hex_hash) == 0) {
//         printf("SHA-256 Hash of '%s':\n%s\n", filepath, hex_hash);
//         return 0;
//     } else {
//         fprintf(stderr, "Failed to calculate SHA-256 hash for '%s'.\n", filepath);
//         return 1;
//     }
// }